﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF;
using System.Reflection;
using PlanningGIS.Framework;

using System.Runtime.InteropServices;
using System.IO;
using System.Xml;
using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;

namespace PlanningGIS.CoordCovert.model
{
    /// <summary>
    /// 四参数结构
    /// </summary>
    public struct Parameter4
    {
        public double a;
        public  double b;
        public double alpa;
        public double namta;
    }

    public class AffineConvert4Parameters : CoordConvertParameters
    {
        //private List<IFeatureClass> pFeatClses;

        private double dx;
        private double dy;
        private double alpa;
        private double namta;
        private object missing = Type.Missing;
        ///// <summary>
        ///// 需要进行转换的要素类集合
        ///// </summary>
        //public List<IFeatureClass> FeatClses
        //{
        //    set { pFeatClses = value; }
        //}

        /// <summary>
        /// X偏移量
        /// </summary>
        public double DX
        {
            set { dx = value; }
        }

        /// <summary>
        /// Y偏移量
        /// </summary>
        public double DY
        {
            set { dy = value; }
        }

        /// <summary>
        /// α旋转角度
        /// </summary>
        public double Alpa
        {
            set { alpa = value; }
        }

        /// <summary>
        /// λ缩放系数
        /// </summary>
        public double Namta
        {
            set { namta = value; }
        }

        private bool flag;
        /// <summary>
        /// true表示用自定义算法转换，false表示用ArcGIS的空间校正工具进行转换
        /// </summary>
        public bool Flag
        {
            set { flag = value; }
        }

        public AffineConvert4Parameters()
        {
            dx = 0;
            dy = 0;
            alpa = 0;
            namta = 1;

            try
            {
                string configPath = AppConfig.ConfigPath + "\\Config.xml";

                if (File.Exists(configPath))
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(configPath);

                    XmlNode xmlNode = xmlDoc.SelectSingleNode("//Config//CoordCovert//四参数变换");
                    if (xmlNode != null)
                    {
                        dx = Double.Parse(xmlNode.Attributes["X偏移量"].Value);
                        dy = Double.Parse(xmlNode.Attributes["Y偏移量"].Value);
                        alpa = Double.Parse(xmlNode.Attributes["旋转角度"].Value);
                        namta = Double.Parse(xmlNode.Attributes["缩放系数"].Value);
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// 数据转换
        /// </summary>
        /// <returns></returns>
        public override bool Operate()
        {
            if (pFeatClses == null || pFeatClses.Count == 0)
            {
               LogHelper.WriteErrLog("请设置需要进行仿射变换的数据！");
                return false;
            }

            if (namta == 0)
            {
               LogHelper.WriteErrLog("缩放系数不能为零！");
                return false;
            }

            setProcessBarMax.Invoke(pFeatClses.Count);

            int countTotal = 0;
            int errorCountTotal = 0;
            int rightCountTotal = 0;

            try
            {

                for (int i = 0; i < pFeatClses.Count; i++)
                {
                    if (stopAnyway)
                        return false;

                    setProcessBarCurrent.Invoke(i + 1);
                    IFeatureClass pFeatCls = pFeatClses[i];

                    try
                    {
                        int allCount = pFeatCls.FeatureCount(null);
                        if (allCount == 0) continue;

                        setSubProcessBarMax(allCount);

                        LogHelper.WriteLog("开始对数据[" + pFeatCls.AliasName + "]进行四参数变换...");

                        IGeoDataset pGeoDataset = pFeatCls as IGeoDataset;
                        IEnvelope pEnvelope = pGeoDataset.Extent;

                        IPoint[] fromPoints = null;
                        IPoint[] toPoints = null;

                        if (!flag)
                        {
                            fromPoints = new IPoint[4];
                            toPoints = new IPoint[4];

                            fromPoints[0] = pEnvelope.LowerLeft;
                            fromPoints[1] = pEnvelope.UpperRight;
                            fromPoints[2]=pEnvelope.LowerRight ;
                            fromPoints [3]=pEnvelope.UpperLeft;

                            toPoints[0] = GetConvertPoint(fromPoints[0]);
                            toPoints[1] = GetConvertPoint(fromPoints[1]);
                            toPoints[2] = GetConvertPoint(fromPoints[2]);
                            toPoints[3] = GetConvertPoint(fromPoints[3]);
                        }
                        //DeleteFeatureclassSpatialIndex(pFeatCls);

                        #region 逐要素转换

                        IFeatureCursor pFeatCur = pFeatCls.Update(null, false);
                        IFeature pFeature = pFeatCur.NextFeature();

                        int count = 0;
                        int rightCount = 0;
                        int errorCount = 0;

                        while (pFeature != null)
                        {
                            if (stopAnyway)
                                return false;

                            count++;
                            SetSubProcessBarCurrent.Invoke(count);

                            if (flag)
                            {
                                if (ConvertFeature(pFeature))
                                {
                                    rightCount++;
                                }
                                else
                                {
                                    errorCount++;
                                }
                            }
                            else
                            {
                                if (ConvertFeature(pFeature, fromPoints, toPoints))
                                {
                                    rightCount++;
                                }
                                else
                                {
                                    errorCount++;
                                }
                            }

                            pFeatCur.UpdateFeature(pFeature);
                            pFeature = pFeatCur.NextFeature();
                            System.Windows.Forms.Application.DoEvents();
                        }

                        if (errorCount == 0)
                        {
                            LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogDone);
                        }
                        else
                        {
                            LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogError);
                        }

                        countTotal += count;
                        rightCountTotal += rightCount;
                        errorCountTotal += errorCount;

                        Marshal.ReleaseComObject(pFeatCur);

                        #endregion

                        //AddFeatureclassSpatialIndex(pFeatCls, 0, 0, 0);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteErrLog("转换数据[" + pFeatCls.AliasName + "]失败：" + ex.Message);

                    }
                }

                if (errorCountTotal == 0)
                {
                    LogHelper.WriteDoneLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }
                else
                {
                   LogHelper.WriteErrLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("转换失败：" + ex.Message);
                return false;
            }
        }

        #region private functions

        /// <summary>
        /// 对单个要素进行转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <returns>转换成功返回true、否则返回false</returns>
        private bool ConvertFeature(IFeature pFeature)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            try
            {
                if (pGeometry.GeometryType == esriGeometryType.esriGeometryMultipoint ||
                    pGeometry.GeometryType == esriGeometryType.esriGeometryPolyline)
                {
                    IPointCollection pPoints = pGeometry as IPointCollection;
                    for (int i = 0; i < pPoints.PointCount; i++)
                    {
                        IPoint pPoint = pPoints.get_Point(i);

                        ConvertPoint(pPoint);

                        pPoints.UpdatePoint(i, pPoint);
                    }
                }
                //如果是面状数据，因为首尾点是同一个点，第一个点做了坐标转换后，最后一个点是不需要再进行转换的，否则该点（首/尾点）就会转换了两次
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPolygon)
                {
                    #region 对于多part的数据，无法转换
                    //IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    //if (pGeometries.GeometryCount != 1)
                    //{
                    //   LogHelper.WriteErrLog("ID为[" + pFeature.OID.ToString() + "]的要素为非单PART图形，目前程序无法进行转换，请您手工操作！");
                    //    return false;
                    //}

                    //IPointCollection pPoints = pGeometry as IPointCollection;
                    //for (int i = 0; i < pPoints.PointCount - 1; i++)
                    //{
                    //    IPoint pPoint = pPoints.get_Point(i);

                    //    ConvertPoint(pPoint);

                    //    pPoints.UpdatePoint(i, pPoint);
                    //}
                    #endregion

                    #region 用以下方式转换后的数据图形都看不到了
                    //IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    //if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    //for (int i = 0; i < pGeometries.GeometryCount; i++)
                    //{
                    //    IPointCollection pPoints = pGeometries.get_Geometry(i) as IPointCollection;

                    //    //最后一个点不用转！
                    //    for (int j = 0; j < pPoints.PointCount - 1; j++)
                    //    {
                    //        IPoint pPoint = pPoints.get_Point(j);

                    //        ConvertPoint(pPoint);

                    //        pPoints.UpdatePoint(j, pPoint);
                    //    }
                    //}
                    #endregion

                    #region 可以较好的转换面图层，但是Z值信息会丢失，转换后全为零
                    IGeometryCollection pGeometryNew = new GeometryBagClass();
                    IPolygon pPolygon = null;
                    ITopologicalOperator2 pTop = null;

                    IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    bool hasZ = false;
                    IFeatureClass pFeatCls = (pFeature.Class as IFeatureClass);
                    IField pField = pFeatCls.Fields.get_Field(pFeatCls.FindField(pFeatCls.ShapeFieldName));
                    IFieldEdit pFieldEdit = pField as IFieldEdit;
                    IGeometryDef pGeometryDef = pFieldEdit.GeometryDef;
                    hasZ = pGeometryDef.HasZ;

                    for (int i = 0; i < pGeometries.GeometryCount; i++)
                    {
                        IGeometry pGeomLow = pGeometries.get_Geometry(i);
                        IGeometry pGeomHigh = Convertor.ConvertLow2High(pGeomLow);

                        if (hasZ)
                        {
                            pGeomHigh = GeometryHelper.Geom2Dto3D(pGeomHigh);
                        }

                        IPointCollection pPoints = pGeomHigh as IPointCollection;

                        //最后一个点不用转！
                        for (int j = 0; j < pPoints.PointCount - 1; j++)
                        {
                            IPoint pPoint = pPoints.get_Point(j);

                            ConvertPoint(pPoint);

                            pPoints.UpdatePoint(j, pPoint);
                        }

                        if (pPolygon == null)
                        {
                            pPolygon = pGeomHigh as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                        else
                        {
                            pTop.IsKnownSimple_2 = false;
                            if (!pTop.IsKnownSimple) pTop.Simplify();

                            ITopologicalOperator2 pTopTemp = pGeomHigh as ITopologicalOperator2;
                            pTopTemp.IsKnownSimple_2 = false;
                            if (!pTopTemp.IsKnownSimple) pTopTemp.Simplify();

                            pPolygon = pTop.Union(pGeomHigh) as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                    }

                    pFeature.Shape = pPolygon;
                    #endregion
                }
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPoint)
                {
                    IPoint pPoint = pGeometry as IPoint;

                    ConvertPoint(pPoint);
                }
                else
                {
                   LogHelper.WriteErrLog("类型不支持！");
                    return false;
                }

                pFeature.Shape = pGeometry;
                //pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 通过ArcGIS提供的校正工具进行四参数转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <param name="fromPoints"></param>
        /// <param name="toPoints"></param>
        /// <returns></returns>
        private bool ConvertFeature(IFeature pFeature, IPoint[] fromPoints, IPoint[] toPoints)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            try
            {
                IAffineTransformation2D3GEN affineTransformation = new AffineTransformation2D() as IAffineTransformation2D3GEN;
                //The method requires as inputs the fromPoints and toPoints array
                affineTransformation.DefineConformalFromControlPoints(ref fromPoints, ref toPoints);

                //The affine transformation can then be used as input in the ITransform2D.Transform method
                ITransform2D transformator = pGeometry as ITransform2D;
                transformator.Transform(esriTransformDirection.esriTransformForward, affineTransformation as ITransformation);

                pFeature.Shape = pGeometry;
                //pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 对某个点进行转换
        /// </summary>
        /// <param name="pPoint"></param>
        private void ConvertPoint(IPoint pPoint)
        {
            //double x = pPoint.X;
            //double y = pPoint.Y;

            //double X, Y;

            //X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            //Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);

            //pPoint.X = X;
            //pPoint.Y = Y;

            //数学上的XY和测绘学上的XY是反过来的！
            double y = pPoint.X;
            double x = pPoint.Y;

            double X, Y;

            X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);

            pPoint.X = Y;
            pPoint.Y = X;

        }

        private IPoint GetConvertPoint(IPoint pPoint)
        {
            //double x = pPoint.X;
            //double y = pPoint.Y;

            //double X, Y;

            //X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            //Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);

            //pPoint.X = X;
            //pPoint.Y = Y;

            //数学上的XY和测绘学上的XY是反过来的！
            double y = pPoint.X;
            double x = pPoint.Y;

            double X, Y;

            X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);

            IPoint pPointNew = new PointClass();
            pPointNew.X = Y;
            pPointNew.Y = X;
            pPointNew.SpatialReference = pPoint.SpatialReference;

            return pPointNew;

        }

        /// <summary>
        /// 计算四参数
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="X1"></param>
        /// <param name="Y1"></param>
        /// <param name="X2"></param>
        /// <param name="Y2"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <param name="alpa"></param>
        /// <param name="namta"></param>
        private void Compute(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2, out double dx, out double dy, out double alpa, out double namta)
        {
            double TanAlpa = ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
            alpa = Math.Atan(TanAlpa);

            namta = (X1 - X2) / ((x1 - x2) * Math.Cos(alpa) - ((y1 - y2) * Math.Sin(alpa)));

            dx = X1 - namta * x1 * Math.Cos(alpa) + namta * y1 * Math.Sin(alpa);

            dy = Y1 - namta * x1 * Math.Sin(alpa) - namta * y1 * Math.Cos(alpa);
        }
        private double ComputeTanAlpa(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2)
        {
            return ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
        }

        /// <summary>
        /// 删除要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <returns></returns>
        private bool DeleteFeatureclassSpatialIndex(IFeatureClass pFeatureClass)
        {
            // Get an enumerator for indexes based on the shape field.
            IIndexes indexes = pFeatureClass.Indexes;
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            IEnumIndex enumIndex = indexes.FindIndexesByFieldName(shapeFieldName);
            enumIndex.Reset();

            // Get the index based on the shape field (should only be one) and delete it.
            IIndex index = enumIndex.Next();

            if (index != null)
            {
                pFeatureClass.DeleteIndex(index);
            }

            return true;
        }

        public void Convert(double x, double y, ref double X, ref double Y)
        {
            X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);
        }


        /*
        public string ConvertPoint(double x1, double y1)
        {
            double y = x1;
            double x = y1;

            double X, Y;

            X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);
            
            return X.ToString() + "," + Y.ToString();

        }

        public string ConvertPoint(double dx, double dy, double namta, double alpa, double x1, double y1)
        {
            double y = x1;
            double x = y1;

            double X, Y;

            X = dx + namta * x * Math.Cos(alpa) - namta * y * Math.Sin(alpa);
            Y = dy + namta * x * Math.Sin(alpa) + namta * y * Math.Cos(alpa);

            return X.ToString() + "," + Y.ToString();
        }
        */

        /// <summary>
        /// 添加要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <param name="pGridOneSize"></param>
        /// <param name="pGridTwoSize"></param>
        /// <param name="pGridThreeSize"></param>
        /// <returns></returns>
        private bool AddFeatureclassSpatialIndex(IFeatureClass pFeatureClass, Double gridOneSize, Double gridTwoSize, Double gridThreeSize)
        {
            // Clone the shape field from the feature class.
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            int shapeFieldIndex = pFeatureClass.FindField(shapeFieldName);
            IFields fields = pFeatureClass.Fields;
            IField sourceField = fields.get_Field(shapeFieldIndex);
            IClone sourceFieldClone = (IClone)sourceField;
            IClone targetFieldClone = sourceFieldClone.Clone();
            IField targetField = (IField)targetFieldClone;

            // Open the geometry definition from the cloned field and modify it.
            IGeometryDef geometryDef = targetField.GeometryDef;
            IGeometryDefEdit geometryDefEdit = (IGeometryDefEdit)geometryDef;
            geometryDefEdit.GridCount_2 = 3;
            geometryDefEdit.set_GridSize(0, gridOneSize);
            geometryDefEdit.set_GridSize(1, gridTwoSize);
            geometryDefEdit.set_GridSize(2, gridThreeSize);

            // Create a spatial index and set the required attributes.
            IIndex newIndex = new IndexClass();
            IIndexEdit newIndexEdit = (IIndexEdit)newIndex;
            newIndexEdit.Name_2 = shapeFieldName + "_Index";
            newIndexEdit.IsAscending_2 = true;
            newIndexEdit.IsUnique_2 = false;

            // Create a fields collection and assign it to the new index.
            IFields newIndexFields = new FieldsClass();
            IFieldsEdit newIndexFieldsEdit = (IFieldsEdit)newIndexFields;
            newIndexFieldsEdit.AddField(targetField);
            newIndexEdit.Fields_2 = newIndexFields;

            // Add the spatial index back into the feature class.
            pFeatureClass.AddIndex(newIndex);

            return true;
        }

        #endregion
    }
}
